package project03;

import java.io.IOException;

/**
 * Title: DataContainer class for CSIS 235 Project 3 
 * Description: CS 235 Project #3 - DataContainer 
 *              A "client" class containing many instances of the Data class. 
 *              see definition of the terms "client" and "server" in the Nino book
 *              This class use the "server" class called Data to store information. 
 *              For good encapsulation: all data members should be private unless 
 *              there is a real good reason for it to be anything else, like public. 
 *              Note that this "client" class is, in turn, a "server" class for the Part3 class.
 * 
 * @author Anthony Varghese
 * @email your.n.here@my.uwrf.edu
 * @date January 30th 2012
 */

public class DataContainer {
	/**
	 * Data members of the DataContainer class 
	 * - infilename - the name of the file we will read 
	 * - outfilename - the name of the file we will write to 
	 * - fileInStream - Scanner object used to read from a file 
	 * - outputStream - PrintStream object used to write dataOut array to a file 
	 */
	private String infilename;
	private String outfilename;
	private java.util.Scanner fileInStream;
	private java.io.PrintStream outputStream;
	/**
	 * - heading stores the text in first line of the input file
 	 * - dataIn stores the data we read in 
	 * - inCount keeps track of how many array locations in dataIn are occupied
	 * - dataOut has the data we will write out
	 * - outCount keeps track of how many array locations in dataOut are occupied
	 */
	private final int NUMBER_OF_COLUMNS= 3; // input file should have 3 columns of dara
	private String heading[] = new String[ NUMBER_OF_COLUMNS ];
	private Data[] dataIn;
	private final int MAX_DATA_SIZE = 100;
	private int inCount;
	private Data[] dataOut;
	private int outCount;

	/**
	 * Constructor to initialize DataContainer object
	 * 
	 * @throws Exception
	 */
	public DataContainer(String infn) {
		dataIn = new Data[ MAX_DATA_SIZE ];
		inCount = 0; // There is no data in dataIn
		dataOut = new Data[ MAX_DATA_SIZE ];
		outCount = 0;
		
		// Open the named file for reading - should set fileInStream
		openInputFile(infn);
		// The first line of the file has column headings
		heading[0] = fileInStream.next();
		heading[1] = fileInStream.next();
		heading[2] = fileInStream.nextLine();

		/*
		 * Read all the data in from the file
		 */
		for (int i=0; i<MAX_DATA_SIZE; i++) {
			// Check if there is any data to be read in
			if (fileInStream.hasNext()) {
				// Read data from the file and store in Data object
				int integerIn = fileInStream.nextInt();
				float floatIn = fileInStream.nextFloat();
				String stringIn = fileInStream.nextLine(); // read in the rest of the data on the line
				if ( stringIn.charAt(0) == '\t' )  // and replace the first tab character
						stringIn = stringIn.replaceFirst("\t", "");
				dataIn[i] = new Data(integerIn, floatIn, stringIn);
				inCount = inCount + 1;
			}
		}
		
		fileInStream.close(); // After are done reading, we can close the file
	}

	/**
	 * Alternate constructor with 2 parameters - input and output files
	 * 
	 * @param inFn
	 * @param outFn
	 * @throws Exception
	 */
	public DataContainer(String inFn, String outFn) {
		this(inFn);

		// Set up the output file for writing.
		openOutFile(outFn);
	}

	/**
	 * sortByRank - change the input data
	 * Make a sorted copy of the "in" array in the "out" array
	 * This method uses the Selection sort algorithm to sort
	 */
	public void sortByRank() {
		/*
		 * Use the "copied" array to keep track of which elements in dataIn
		 * have already been copied into dataOut
		 */
		boolean copied[] = new boolean[ dataIn.length ]; // assume initial values == false
		
		for (int i=0; i<dataIn.length; i++) {
			int minIndex = 0;
			while ( dataIn[minIndex] == null || copied[ minIndex] ) // skip the ones that have already been copied
				minIndex++;
			int minValue = dataIn[minIndex].getX();
			/*
			 * Find the smallest item in dataIn
			 *  that has not yet been copied to dataOut
			 */
			for (int j=0; j<dataIn.length; j++) {
				if (dataIn[j] == null || copied[j] )
					continue;
				int vj = dataIn[j].getX();
				if (vj < minValue){
					minIndex = j;
					minValue = vj;
				}
			}
			/*
			 * Store the smallest item in the dataOut array.
			 */
			dataOut[i] = new Data( minValue, dataIn[minIndex].getY(), dataIn[minIndex].getZ() );
			copied[ minIndex ] = true;
			outCount = outCount + 1;
		}
	}

	/**
	 * toString - convert the dataIn array to a String
	 */
	public String toString() {
		return toString( dataIn );
	}

	/**
	 * output - convert the dataOut array to a String
	 */
	public String output() {
		return toString( dataOut );
	}

	/**
	 * toString - convert a Data array to a String
	 */
	public String toString(Data[] array) {
		String result = "\t" + heading[0] + "\t" + heading[1] + "\t" + heading[2];
		for (Data d : array)
			result = result + "\n   " + d;
		return result;
	}

	/**
	 * writeToFile - write all of the data to an output file
	 * 
	 * @param outfilename2
	 */
	public void writeToFile(String outFn) {
		// Set up the output file for writing.
		openOutFile(outFn);

		if (dataOut != null) {
			// Write all the data
			for (Data d : dataOut)
				if (d != null)
					outputStream.println(d);
		}

		// Close to save changes
		closeFile(outputStream);
	}

	/**
	 * openFile - a method that opens a file for reading.
	 * 
	 * @param inFn - name of input file
	 */
	private java.util.Scanner openInputFile(String inFn) {
		infilename = inFn;
		if (infilename == null || infilename.isEmpty())
			return null;
		java.io.FileInputStream fIn = null;
		try {
			fIn = new java.io.FileInputStream(infilename);
		} catch (java.io.FileNotFoundException fex) {
			System.err.println(" Error! This program is trying to open "
					+ infilename
					+ " but it does not exist! Or it's some place else.");
			return null;
		}
		fileInStream = new java.util.Scanner(fIn);
		return fileInStream;
	}

	/**
	 * openOutFile - a method that opens a file for writing.
	 * 
	 * @param outFn
	 *            - name of output file
	 */
	private java.io.PrintStream openOutFile(String outFn) {
		outfilename = outFn;
		if (outfilename == null || outfilename.isEmpty())
			return null;
		java.io.FileOutputStream fOut = null;
		try {
			fOut = new java.io.FileOutputStream(outfilename);
		} catch (java.io.FileNotFoundException fex) {
			System.err.println(" Error! This program is trying to open "
					+ outfilename
					+ " but it does not exist! Or it's some place else.");
			return null;
		}
		outputStream = new java.io.PrintStream(fOut);
		return outputStream;
	}

	/**
	 * closeFile - a method that closes a file that was previously opened for
	 * reading or writing.
	 * 
	 * @param fs
	 *            - an open FileInputStream or FileOutputStream object
	 */
	private void closeFile(java.io.Closeable fs) {
		if (fs == null)
			return; // nothing to close

		try {
			fs.close();
		} catch (IOException e) {
			System.err.println(" Error! This program is trying to close a file but something went wrong.");
		}
	}
}
